package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.system.model.dto.CourseCategoryDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.constant.CoursePubTemplateKey;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.*;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.*;
import com.xuecheng.feign.system.SystemApiAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private CourseMarketService courseMarketService;

    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private SystemApiAgent systemApiAgent;

    @Autowired
    private CourseTeacherService courseTeacherService;

    @Autowired
    private CoursePubMsgService coursePubMsgService;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Value("${course.publish.exchange}")
    private String exchange;
    @Value("${course.publish.routingkey}")
    private String routingkey;


    /*
    * 业务分析（主方法）
    *   1.判断关键数据
    *   2.构建Coursepub数据
    *   3.根据CoursePub数据生成数据模型DatMap
    *   4.将数据模型daatamp返回
    *
    * */
    @Transactional
    public Map<String, Object> previewCourse(Long courseBaseId, Long companyId) {
        //1.判断关键数据
        if(ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_110001);
        }
        //2.构建Coursepub数据
        CoursePub coursePub=generateCoursePub(courseBaseId,companyId,false);


        //3.根据CoursePub数据生成数据模型DatMap
        Map<String,Object> dataMap =generateDataMap(coursePub);

        //4.将数据模型daatamp返回

        return dataMap;
    }

    /*
    * 业务分析:
    *   0.是否开启事务
    *   1.判断关键数据
    *       courseBaseId    companyId
    *   2.判断业务数据和构建coursepub
    *   3.创建课程发布消息表
    *       CoursePubMsg
    *   4.消息发送给mq
    *       执行消息唯一性：唯一标识-coursePubid
    *       声明confirmcallback
    *       构建发送的消息：coursePubMsg-jsonString
    *
    *
    * */
    @Transactional
    public void publishCourse(Long courseBaseId, Long companyId, boolean isReSend) {

        //1.判断关键数据
        //    courseBaseId    companyId
        if(ObjectUtils.isEmpty(companyId) || ObjectUtils.isEmpty(courseBaseId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }


        CoursePubMsg coursePubMsg=null;
        //定时器重新发送消息获得消息表中的数据
        if(isReSend){
            LambdaQueryWrapper<CoursePubMsg> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(CoursePubMsg::getCourseId,courseBaseId);

            coursePubMsg=coursePubMsgService.getOne(queryWrapper);
        }else {
            //2.判断业务数据和构建coursepub
            //  课程发布要求：课程基础信息的审核状态--审核通过
            CoursePub coursePub = generateCoursePub(courseBaseId, companyId, true);
            //3.创建课程发布消息表
            //    CoursePubMsg
            //  如果没有创建数据
            if(ObjectUtils.isEmpty(coursePubMsg)){
                coursePubMsg = CoursePubMsg.builder().courseId(coursePub.getCourseId())
                        .pubId(coursePub.getId())
                        .pubStatus(CoursePubMsg.UNSENT)
                        .pubName(coursePub.getName())
                        .companyId(coursePub.getCompanyId())
                        .build();

                boolean result = coursePubMsgService.save(coursePubMsg);
                if(!result){
                    ExceptionCast.cast(ContentErrorCode.E_120202);
                }
            }else {
                //如果有：消息不存在，不用再次发送消息
                return;
            }
        }


        //4.消息发送给mq
        //    执行消息唯一性：唯一标识-coursePubid
        //    声明confirmcallback
        //    构建发送的消息：coursePubMsg-jsonString

        CorrelationData correlationData = new CorrelationData(coursePubMsg.getPubId().toString());

        //异步操作
        correlationData.getFuture().addCallback(
                confirm ->{
                    if(confirm.isAck()){
                        executeChangeLocalStatusData(correlationData.getId());
                    }else {
                        log.error("mq交换机接受消息失败，消息id ：{} ",correlationData.getId());
                    }
                },
                //mq服务一次会调用次方法
                throwable -> {
                    log.error("消息发送失败，mq服务异常，消息的id：{}，errormsg：{}",correlationData.getId(),throwable.getMessage());
                }
        );

        String jsonString = JsonUtil.objectTojson(coursePubMsg);

        /*
        * 参数：
        *   1.交换机的名称
        *   2.rontingkey
        *   3.消息
        *       消息表的数据：CoursePubMsg（json）
        *   4.CorrelationData（设置confirm callback）
        *       接受交互机处理数据的结果（应答机制）
        *           ack：交换机处理成功--> 修改业务数据和消息表的状态
        *           nack：交换机处理失败--> 记录错误消息，并不操作业务数据和消息表
        * */

        rabbitTemplate.convertAndSend(exchange,routingkey,jsonString,correlationData);



    }

    /*
    * 执行本地事务修改成功后的本地数据状态
    *
    * 业务分析：
    *   0.是否开启事务
    *   1.修改课程发布消息表数据
    *       未发送-->已发送
    *   2.修改课程基础信息数据
     *      课程审核状态：已发布
    * */
    @Transactional
    public void executeChangeLocalStatusData(String coursePubIdStr) {

        //1.修改课程发布消息表数据
        //   未发送-->已发送
        Long coursePubId = new Long(coursePubIdStr);

        LambdaUpdateWrapper<CoursePubMsg> pubMsgUpdateWrapper = new LambdaUpdateWrapper<>();
        pubMsgUpdateWrapper.set(CoursePubMsg::getPubStatus,CoursePubMsg.SENT);
        pubMsgUpdateWrapper.set(CoursePubMsg::getChangeDate, LocalDateTime.now());
        pubMsgUpdateWrapper.eq(CoursePubMsg::getPubId,coursePubId);

        coursePubMsgService.update(pubMsgUpdateWrapper);

        //2.修改课程基础信息数据
        //  课程审核状态：已发布
        CoursePubMsg coursePubMsg = coursePubMsgService.getById(coursePubId);
        LambdaUpdateWrapper<CourseBase> baseUpdateWrapper = new LambdaUpdateWrapper<>();
        baseUpdateWrapper.set(CourseBase::getAuditStatus,CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode());
        baseUpdateWrapper.set(CourseBase::getChangeDate,LocalDateTime.now());
        baseUpdateWrapper.eq(CourseBase::getId,coursePubMsg.getCourseId());

        courseBaseService.update(baseUpdateWrapper);


    }

    private Map<String, Object> generateDataMap(CoursePub coursePub) {
        //1.构建数据模型dataMap
        HashMap<String, Object> dataMap = new HashMap<>();

        //课程营销
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);

        dataMap.put(CoursePubTemplateKey.COURSEMARKET,courseMarket);

        //课程计划
        String teachplanJsonString = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJsonString, TeachplanDTO.class);

        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE,teachplanDTO);

        //课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS,values);

        //课程教师
        String teachersJsonString = coursePub.getTeachers();
        List<CourseTeacher> teachers = JsonUtil.jsonToList(teachersJsonString,CourseTeacher.class);

        dataMap.put(CoursePubTemplateKey.TEACHERS,teachers);


        return dataMap;
    }

    /*
    * 业务分析：
    *   1.判断业务数据
    *       课程基础信息
    *           判断是否是同一家机构
    *           判断是否删除
    *           判断审核撞状态
    *           查询条件：courseId，companyId
    *       课程营销
    *           判断是否存在（课程营销是和课程基础信息属于用一个业务操作）
    *           查询条件：courseId，companyId
    *       课程计划（一个课程的课程计划树形结构）
    *            查询条件：courseid、companyid
    *       课程分类
    *           从系统管理服务中获得课程分类数据
    *           查询条件：courseCtegoryId（mt，st）
    *       课程教师信息
    *           查询条件：courseId
    *
    *   2.构建CoursePub数据
    *       courseBase
    *       teaachplan
    *       courrseMarket
    *       courseTeacher
    *
    *   保存coursepub数据
    *       潘墩是否存在
    *           如果存在：更新数据
    *           不存在：添加操作
    *
    *   3.返回coursePub
    *
    *
    * */
    private CoursePub generateCoursePub(Long courseBaseId, Long companyId,boolean isPublish) {
        //1.判断业务数据
        //    课程基础信息
        //        判断是否是同一家机构
        //        判断是否删除
        CourseBaseDTO courseBase = courseBaseService.getCourseById(courseBaseId, companyId);


        //        判断审核撞状态
        String auditStatus = courseBase.getAuditStatus();
        if(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
            CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
            CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ){
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }


        //    课程营销
        //        判断是否存在（课程营销是和课程基础信息属于用一个业务操作
        //        查询条件：courseId，companyId
        LambdaQueryWrapper<CourseMarket> courseMarketQueryWrapper = new LambdaQueryWrapper<>();
        courseMarketQueryWrapper.eq(CourseMarket::getCourseId,courseBaseId);

        CourseMarket courseMarket = courseMarketService.getOne(courseMarketQueryWrapper);//根据wrapper查询一条记录

        if(ObjectUtils.isEmpty(courseMarket)){
            ExceptionCast.cast(ContentErrorCode.E_120101);
        }
        String marketJsonString=JsonUtil.objectTojson(courseMarket);

        //课程计划（一个课程的课程计划树形结构）
        //     查询条件：courseid、companyid
        TeachplanDTO teachplan = teachplanService.queryTreenodesByCourseId(courseBaseId, companyId);

        String teachplanJsonString = JsonUtil.objectTojson(teachplan);


        //    课程分类
        //        从系统管理服务中获得课程分类数据
        //        查询条件：courseCtegoryId（mt，st）
        String mt = courseBase.getMt();
        String st = courseBase.getSt();

        RestResponse<CourseCategoryDTO> mtResponse = systemApiAgent.getById(mt);
        if(!(mtResponse.isSuccessful())){
            ExceptionCast.castWithCodeAndDesc(mtResponse.getCode(),mtResponse.getMsg());
        }

        CourseCategoryDTO mtCategory = mtResponse.getResult();

        RestResponse<CourseCategoryDTO> stResponse = systemApiAgent.getById(st);

        if(!(stResponse.isSuccessful())){
            ExceptionCast.castWithCodeAndDesc(stResponse.getCode(),stResponse.getMsg());
        }
        CourseCategoryDTO stCategory = stResponse.getResult();


        //    课程教师信息
        //        查询条件：courseId
        LambdaQueryWrapper<CourseTeacher> teachqueryWrapper = new LambdaQueryWrapper<>();
        teachqueryWrapper.eq(CourseTeacher::getCourseId,courseBaseId);

        List<CourseTeacher> teachers = courseTeacherService.list(teachqueryWrapper);

        //转换为json
        String teachersJsonString=null;
        if(!(ObjectUtils.isEmpty(teachers))){
            teachersJsonString=JsonUtil.objectTojson(teachers);
        }


        //2.构建CoursePub数据
        //    courseBase
        //    teaachplan
        //    courrseMarket
        //    courseTeacher
        CoursePub coursePub = CoursePubConvert.INSTANCE.courseBase2coursePub(courseBase);
        coursePub.setTeachplan(teachplanJsonString);
        coursePub.setMarket(marketJsonString);
        coursePub.setTeachers(teachersJsonString);

        //课程营销冗余字段
        coursePub.setPrice(courseMarket.getPrice());
        coursePub.setCharge(courseMarket.getCharge());

        //课程分类的信息完善
        coursePub.setMtName(mtCategory.getName());
        coursePub.setStName(stCategory.getName());

        //课程发布数据要课程基础信息要关联
        coursePub.setCourseId(courseBaseId);



        //保存coursepub数据
        //    判断是否存在
        //        如果存在：更新数据
        //        不存在：添加操作
        LambdaQueryWrapper<CoursePub> coursePubQueryWrapper = new LambdaQueryWrapper<>();
        coursePubQueryWrapper.eq(CoursePub::getCourseId,courseBaseId);
        CoursePub po = this.getOne(coursePubQueryWrapper);

        boolean result=false;
        if(ObjectUtils.isEmpty(po)){
            //        不存在：添加操作
            result=this.save(coursePub);
        }else {
            //        如果存在：更新数据
            coursePub.setId(po.getId());
            result=this.updateById(coursePub);
        }

        if(!result){
            ExceptionCast.cast(ContentErrorCode.E_120205);
        }


        //3.返回coursePub
        return coursePub;
    }
}
